
/* 
 * Copyright 2001-2009 Terracotta, Inc. 
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 
 * use this file except in compliance with the License. You may obtain a copy 
 * of the License at 
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0 
 *   
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 
 * License for the specific language governing permissions and limitations 
 * under the License.
 * 
 */

package cn.wapache.time.trigger;

import cn.wapache.exception.Exceptions;
import cn.wapache.time.calendar.Calendar;
import cn.wapache.time.trigger.Trigger.TriggerState;
import cn.wapache.timed.i18n.Si18n;

/**
 * <p>
 * The base abstract class to be extended by all <code>Trigger</code>s.
 * </p>
 * 
 * <p>
 * <code>Triggers</code> s have a name and group associated with them, which
 * should uniquely identify them within a single <code>{@link Scheduler}</code>.
 * </p>
 * 
 * <p>
 * <code>Trigger</code>s are the 'mechanism' by which <code>Job</code> s
 * are scheduled. Many <code>Trigger</code> s can point to the same <code>Job</code>,
 * but a single <code>Trigger</code> can only point to one <code>Job</code>.
 * </p>
 * 
 * <p>
 * Triggers can 'send' parameters/data to <code>Job</code>s by placing contents
 * into the <code>JobDataMap</code> on the <code>Trigger</code>.
 * </p>
 *
 * @author James House
 * @author Sharada Jambula
 */
public abstract class AbstractTrigger<T extends Trigger> implements Trigger {

    private static final long serialVersionUID = -3904243490805975570L;

    private String id;
    private String name;
    private String description;

    private String jobId;
    private String calendarId = null;

    private int priority = DEFAULT_PRIORITY;
    protected TriggerState state = TriggerState.CREATE;
    private MisfirePolicy misfireInstruction = MisfirePolicy.SMART;
    
    protected long startTime;
    protected long endTime;

    /**
     * 
     * =0: 初始值/正常结束值, 
     * <0: 发生错误时的取值
     * >0: 正常触发时间
     * 
     */
    protected long nextFireTime;

    protected long previousFireTime;

    protected int timesTriggered = 0;


    public void validate() {
        Si18n.validateProperties("id",this.id,"name",this.name,"jobId",this.jobId);
    }

    public boolean isInitialized(){
    	return state != TriggerState.CREATE;
    }

    public boolean initialize(Calendar calendar){
    	state = TriggerState.NORMAL;
    	nextFireTime = computeFirstFireTime(calendar);
    	updateState();
    	return this.isEnabled();
    }
    
	protected abstract void updateState();
	
    ///////////////////////////////////////////////////////////////////////////
    // getter setter
    ///////////////////////////////////////////////////////////////////////////

    public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getJobId() {
		return jobId;
	}

	public void setJobId(String jobId) {
		this.jobId = jobId;
	}

	public String getCalendarId() {
		return calendarId;
	}

	public void setCalendarId(String calendarId) {
		this.calendarId = calendarId;
	}

	public String getName() {
        return name;
    }

    public void setName(String name) {
    	this.name = Si18n.validateArg("name", this.name, name);
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public int getPriority() {
        return priority;
    }

    public void setPriority(int priority) {
        this.priority = priority;
    }

    @Override
    public long getStartTime() {
        return startTime;
    }

    @Override
    public void setStartTime(long startTime) {
        if (startTime <= 0 ) {
            throw new IllegalArgumentException("Start time cannot be null");
        }

        long eTime = getEndTime();
        if (eTime >0 && startTime >0 && eTime < startTime) {
            throw new IllegalArgumentException(
                "End time cannot be before start time");    
        }

        this.startTime = startTime;
    }

    @Override
    public long getEndTime() {
        return endTime;
    }

    @Override
    public void setEndTime(long endTime) {
        long sTime = getStartTime();
        if (sTime > 0 && endTime > 0 && sTime > endTime) {
            throw new IllegalArgumentException(
                    "End time cannot be before start time");
        }

        this.endTime = endTime;
    }


    /**
     * <p>
     * Returns the previous time at which the <code>SimpleTrigger</code> 
     * fired. If the trigger has not yet fired, <code>null</code> will be
     * returned.
     */
    @Override
    public long getPreviousFireTime() {
        return previousFireTime;
    }
    /**
     * <p>
     * Set the previous time at which the <code>SimpleTrigger</code> fired.
     * </p>
     * 
     * <p>
     * <b>This method should not be invoked by client code.</b>
     * </p>
     */
    public void setPreviousFireTime(long previousFireTime) {
        this.previousFireTime = previousFireTime;
    }
    
    @Override
    public long getNextFireTime() {
        return nextFireTime;
    }

    /**
     * <p>
     * Set the next time at which the <code>SimpleTrigger</code> should fire.
     * </p>
     * 
     * <p>
     * <b>This method should not be invoked by client code.</b>
     * </p>
     */
    public void setNextFireTime(long nextFireTime) {
        this.nextFireTime = nextFireTime;
    }

    public Trigger.MisfirePolicy getMisfireInstruction() {
        return misfireInstruction;
    }
    public void setMisfireInstruction(Trigger.MisfirePolicy misfireInstruction) {
        if (!validateMisfireInstruction(misfireInstruction)) {
            throw Exceptions.illegalArgument(Si18n.trigger_misfire_instruction_is_invalid, misfireInstruction);
        }
        this.misfireInstruction = misfireInstruction;
    }
    protected abstract boolean validateMisfireInstruction(Trigger.MisfirePolicy candidateMisfireInstruction);


	@Override
	public TriggerState getState() {
		return state;
	}

	@Override
	public void setState(TriggerState state) {
		this.state = state;
	}

    ///////////////////////////////////////////////////////////////////////////
    // Cloneable
    ///////////////////////////////////////////////////////////////////////////

    @SuppressWarnings("unchecked")
	public AbstractTrigger<T> clone(){
    	try {
			return (AbstractTrigger<T>) super.clone();
		} catch (CloneNotSupportedException e) {
			throw Si18n.newException(Si18n.clone_object_failed);
		}
    }

    ///////////////////////////////////////////////////////////////////////////
    // 废除的方法
    ///////////////////////////////////////////////////////////////////////////

    
    /**
     * <p>
     * This method should not be used by the Quartz client.
     * </p>
     * 
     * <p>
     * Called after the <code>{@link Scheduler}</code> has executed the
     * <code>{@link org.quartz.api.job.JobDetail}</code> associated with the <code>Trigger</code>
     * in order to get the final instruction code from the trigger.
     * </p>
     * 
     * @param context
     *          is the <code>JobExecutionContext</code> that was used by the
     *          <code>Job</code>'s<code>execute(xx)</code> method.
     * @param result
     *          is the <code>JobExecutionException</code> thrown by the
     *          <code>Job</code>, if any (may be null).
     * @return one of the CompletedExecutionInstruction constants.
     * 
     * @see org.quartz.api.trigger.Trigger.CompletedExecutionInstruction
     * @see #triggered(Calendar)
     */
//    public CompletedExecutionInstruction executionComplete(JobExecutionContext context,
//                                          JobExecutionException result)
//    {
//        if (result != null && result.refireImmediately()) {
//            return CompletedExecutionInstruction.RE_EXECUTE_JOB;
//        }
//    
//        if (result != null && result.unscheduleFiringTrigger()) {
//            return CompletedExecutionInstruction.SET_TRIGGER_COMPLETE;
//        }
//    
//        if (result != null && result.unscheduleAllTriggers()) {
//            return CompletedExecutionInstruction.SET_ALL_JOB_TRIGGERS_COMPLETE;
//        }
//    
//        if (!mayFireAgain()) {
//            return CompletedExecutionInstruction.DELETE_TRIGGER;
//        }
//    
//        return CompletedExecutionInstruction.NOOP;
//    }

}
